import { RefPlaceholder } from '../../agStack/interfaces/agComponent';
import { _clearElement } from '../../agStack/utils/dom';
import { _exists } from '../../agStack/utils/generic';
import type { ElementParams } from '../../utils/element';
import { Component } from '../../widgets/component';
import type { ICellRenderer } from './iCellRenderer';

const ARROW_UP = '\u2191';
const ARROW_DOWN = '\u2193';

const AnimateShowChangeCellRendererElement: ElementParams = {
    tag: 'span',
    children: [
        { tag: 'span', ref: 'eDelta', cls: 'ag-value-change-delta' },
        { tag: 'span', ref: 'eValue', cls: 'ag-value-change-value' },
    ],
};

export class AnimateShowChangeCellRenderer extends Component implements ICellRenderer {
    private lastValue: any;

    private readonly eValue: HTMLElement = RefPlaceholder;
    private readonly eDelta: HTMLElement = RefPlaceholder;

    private refreshCount = 0;

    constructor() {
        super(AnimateShowChangeCellRendererElement);
    }

    public init(params: any): void {
        this.refresh(params, true);
    }

    private showDelta(params: any, delta: number): void {
        const absDelta = Math.abs(delta);
        const valueFormatted = params.formatValue(absDelta);

        const valueToUse = _exists(valueFormatted) ? valueFormatted : absDelta;

        const deltaUp = delta >= 0;

        const eDelta = this.eDelta;
        if (deltaUp) {
            eDelta.textContent = ARROW_UP + valueToUse;
        } else {
            // because negative, use ABS to remove sign
            eDelta.textContent = ARROW_DOWN + valueToUse;
        }

        eDelta.classList.toggle('ag-value-change-delta-up', deltaUp);
        eDelta.classList.toggle('ag-value-change-delta-down', !deltaUp);
    }

    private setTimerToRemoveDelta(): void {
        // the refreshCount makes sure that if the value updates again while
        // the below timer is waiting, then the below timer will realise it
        // is not the most recent and will not try to remove the delta value.
        this.refreshCount++;
        const refreshCountCopy = this.refreshCount;
        this.beans.frameworkOverrides.wrapIncoming(() => {
            window.setTimeout(() => {
                if (refreshCountCopy === this.refreshCount) {
                    this.hideDeltaValue();
                }
            }, 2000);
        });
    }

    private hideDeltaValue(): void {
        this.eValue.classList.remove('ag-value-change-value-highlight');
        _clearElement(this.eDelta);
    }

    public refresh(params: any, isInitialRender: boolean = false): boolean {
        const { value, valueFormatted } = params;

        const { eValue, lastValue, beans } = this;
        if (value === lastValue) {
            return false;
        }

        if (_exists(valueFormatted)) {
            eValue.textContent = valueFormatted;
        } else if (_exists(value)) {
            eValue.textContent = value;
        } else {
            _clearElement(eValue);
        }

        // we don't show the delta if we are in the middle of a filter. see comment on FilterManager
        // with regards processingFilterChange
        if (beans.filterManager?.isSuppressFlashingCellsBecauseFiltering()) {
            return false;
        }

        const numericValue = value && typeof value === 'object' && 'toNumber' in value ? value.toNumber() : value;
        const numericLastValue =
            lastValue && typeof lastValue === 'object' && 'toNumber' in lastValue ? lastValue.toNumber() : lastValue;

        if (numericValue === numericLastValue) {
            return false;
        }

        if (typeof numericValue === 'number' && typeof numericLastValue === 'number') {
            const delta = numericValue - numericLastValue;
            this.showDelta(params, delta);
        }

        // highlight the current value, but only if it's not new, otherwise it
        // would get highlighted first time the value is shown
        if (lastValue) {
            eValue.classList.add('ag-value-change-value-highlight');
        }

        if (!isInitialRender) {
            this.setTimerToRemoveDelta();
        }

        this.lastValue = value;

        return true;
    }
}
